package cate.game.role.farm.island;

import cate.common.table.d.*;
import cate.common.table.farm.island.row.*;
import cate.common.util.GameResult;
import cate.game.attr.FightAttr;
import cate.game.fight.task.FightTask;
import cate.game.mail.po.MailBuilder;
import cate.game.play.param.FightParam;
import cate.game.play.param.FightSideParam;
import cate.game.play.param.SkillAppend;
import cate.game.play.part.FightSide;
import cate.game.play.proce.play.PlayPO;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.role.Role;
import cate.game.role.bag.hero.Hero;
import cate.game.role.farm.AbstractFarm;
import cate.game.role.farm.island.cell.*;
import cate.game.role.farm.island.msg.IslandChallengeResultResp;
import cate.game.role.farm.island.msg.IslandDataResp;
import cate.game.role.farm.island.po.Gain;
import cate.game.role.farm.island.po.IslandMap;
import cate.game.role.farm.island.po.IslandStateRecord;
import cate.game.role.fight.FuncPosition;
import cate.game.role.fight.Position;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;


public class Island extends AbstractFarm {
    @NoteField(value = "当前记录的周期", detail = "根据开服天数来推算 2天算一个周期")
    public int weekNo;

    @NoteField(value = "选择的类型 1普通 2困难 3噩梦")
    public int difficulty;

    @NoteField(value = "当前迷宫地图", detail = "通过一层后重置")
    public IslandMap map;

    @NoteField(value = "迷宫状态记录", detail = "记录本轮冒险的一些状态")
    public IslandStateRecord record;

    @NoteField(value = "当前一期的共享等级")
    public int shareLevel;

    @NoteField(value = "当前一期玩家英雄最高等级")
    public int highestLevel;

    @NoteField("累积的失败扣分")
    public double failScore;

    @NoteField("累计的成功加分")
    public double successScore;

    @NoteField("通关次数<难度, 次数>")
    public Map<Integer, Integer> passTimes;

    @JsonIgnore
    @NoteField("是否已记录灵界秘藏任务")
    public boolean ifHoard;

    @Override
    @JsonIgnore
    public int getFightFuncId() {
        return GDFunc.ISLAND;
    }

    @Override
    public void initialize(Role role){
        super.initialize(role);
        if (map == null) {
            map = new IslandMap();
        }
        map.initialize(role);
        if (record == null) {
            record = new IslandStateRecord();
        }
        record.initialize(role);
        if (passTimes == null) {
            passTimes = new HashMap<>();
        }
    }

    @Override
    public void onEnter(){
        checkBuildMap(true);
    }

    @Override
    public void onDaySpan(boolean silence) {
        checkBuildMap(silence);
    }

    /** 检测是否要构建地图 */
    private synchronized void checkBuildMap(boolean silence) {
        if(!isOpen()){
            return;
        }
        int oldWeekNo = this.weekNo;
        int currentWeekNo = role.getGame().farm.local.island.data.weekNo;
        // 开启了新的一期
        if (this.weekNo != currentWeekNo) {
            this.weekNo = currentWeekNo;
            reset();
            if (!silence) {
                noticeUpdate();
            }
            // 给能玩的玩家发邮件
            if (oldWeekNo > 0 && isOpen()) {
                role.getMail().add(
                        new MailBuilder()
                                .setContentId(GDMail.Tid.ISLAND_NOTICE)
                                .build());
            }
        }
    }

    // 重置天界迷宫
    private void reset() {
        if (difficulty > 0) {
            if (passIsland()) {
                successScore += map.calcSuccessScore();
            } else {
                failScore += map.calFailScore();
            }
        }
        //强制结束之前的战斗
        FightTask islandTask = role.getGame().fight.task.removeTask(role, GDFunc.ISLAND);
        if (islandTask != null) {
            IslandChallengeResultResp resultResp = new IslandChallengeResultResp();
            resultResp.win = false;
            resultResp.over = true;
            role.sendNow(resultResp);
        }
        map.mapHis.clear();
        record.clear();
        difficulty = 0;
        this.shareLevel = role.getRes().worldTree.heroShare.shareLvlMax;
        this.highestLevel = role.getRes().worldTree.heroShare.lvlMax;

        Position advPosition = role.getFight().getPosition(GDFunc.ADVENTURE);
        if (advPosition != null) {
            FuncPosition funcPosition = new FuncPosition();
            funcPosition.init();
            funcPosition.funcId = getFightFuncId();
            funcPosition.positions.add(advPosition.copy());
            role.getGame().fight.position.updatePosition(role, funcPosition);
        }

        ifHoard = false;
    }

    public boolean passIsland(){
        IslandfloorRow row = role.getGame().table.island.floor.get(map.floor);
        return row != null && row.nextFloors.isEmpty() && map.end();
    }

    public EcResult<Void> chooseMap(int difficulty){
        EcResult<Void> r = new EcResult<>();
        if(this.difficulty != 0){
            return r.fail("不可重复选择难度");
        }
        if(difficulty != GDIsland.Difficulty.NORMAL
                && difficulty != GDIsland.Difficulty.CHASM
                && difficulty != GDIsland.Difficulty.DIFFICULT){
            return r.fail("选择难度有误");
        }
        IslandfloorRow initFloorRow = role.getGame().table.island.floor.getInitFloor(difficulty);
        if (initFloorRow == null) {
            return r.fail("选择难度有误");
        }
        if(initFloorRow.levelLimit > role.getBase().level){
            return r.fail(initFloorRow.levelLimit + "解锁该难度");
        }
        this.difficulty = difficulty;
        map.buildMap(initFloorRow.id);
        record.clear();
        noticeUpdate();
        return r;
    }

    /**
     * 尝试一次操作
     *
     * @param x 横坐标
     * @param param 附加参数
     */
    public void action(int x, int param) {
        if(difficulty == 0){
            role.getGame().notice.message(role, "请先选择副本难度");
            return;
        }
        IslandCell cell = map.getCell(x);
        if (cell == null) {
            role.getGame().notice.message(role, "没有这个格子");
            return;
        }
        if (!map.moveAvaible(x)) {
            role.getGame().notice.message(role, "只能走相邻的格子");
            return;
        }
        if (isFighting()) {
            role.getGame().notice.message(role, "当前正在战斗中");
            return;
        }
        // 根据格子类型做不同的判断
        if (GDIsland.CellType.isFightCell(cell.type)) {
            EcResult<Void> fightRes = challenge((FightSideCell) cell, param);
            if (!fightRes.ok()) {
                role.getGame().notice.message(role, fightRes.message);
            }
        } else {
            GameResult<Void> r = normalHandle(cell, param);
            if (!r.ok()) {
                role.getGame().notice.message(role, r.message);
                return;
            }
            // 后续处理 （判断是否要往前走一步 是否修改地图）
            map.afterAction(x,param);
        }
    }

    /** 普通格子的处理（不包括战斗） */
    private GameResult<Void> normalHandle(IslandCell cell, int param) {
        switch (cell.type) {
            case GDIsland.CellType.GUARD:
            case GDIsland.CellType.ELITE_GUARD:
            case GDIsland.CellType.BOSS:
                return challenge((FightSideCell) cell, param);
            case GDIsland.CellType.GAIN:
                return record.pickGain((GainCell) cell, param);
            case GDIsland.CellType.AID_HERO:
                return record.pickHero((AidHeroCell) cell, param);
            case GDIsland.CellType.HEAL:
                return record.heal();
            case GDIsland.CellType.REVIVE:
                return record.reviveSingle();
            case GDIsland.CellType.MALL:
                return buyItem((MallCell) cell, param);
            default:
                return new GameResult<Void>().fail();
        }
    }

    @JsonIgnore
    private boolean isOver(FightSideCell fightSideCell, PlayPO play){
        if(play != null && play.winner != GDFight.Team.A){
            return true;
        }
        return absoluteWin(fightSideCell);
    }

    private boolean absoluteWin(FightSideCell fightSideCell){
        IslandGuardRow row = role.getGame().table.island.guard.get(fightSideCell.guardId);
        return fightSideCell.winTimes >= (row.hasTeam2 ? 2 : 1);
    }

    private boolean adheadOver(FightSideCell fightSideCell, PlayPO play) {
        if (play.winner != GDFight.Team.A) {
            return true;
        }
        IslandGuardRow row = role.getGame().table.island.guard.get(fightSideCell.guardId);
        return fightSideCell.winTimes + 1 >= (row.hasTeam2 ? 2 : 1);
    }

    public GameResult<Void> beginChallenge(FightSideCell fightSideCell, IslandGuardGroupRow row, PlayPO play){
        GameResult<Void> r = new GameResult<>();
        if(isOver(fightSideCell, play)){
            afterChallenge(fightSideCell, play == null || play.winner == GDFight.Team.A);
        }else{
            //之前打过一场 发一个结果通知结束了
            if (play != null) {
                IslandChallengeResultResp resultResp = new IslandChallengeResultResp();
                resultResp.win = true;
                resultResp.over = false;
                role.sendNow(resultResp);
            }
            IslandFTC context = new IslandFTC(role, row, fightSideCell);
            context.funcId = getFightFuncId();
            context.posNum = (byte) (fightSideCell.winTimes + 1);
            context.playStory.playIndex = context.posNum;
            IslandGuardRow guardRow = role.getGame().table.island.guard.get(fightSideCell.guardId);
            context.playStory.playMax = guardRow.hasTeam2 ? 2 : 1;
            context.playStory.level = guardRow.id;
            for (int i = 1; i < context.posNum; i++) {
                context.playStory.preWinner.add(GDFight.Team.A);
            }
            FightParam fightParam = new FightParam();
            handleGain(fightParam.sideA);
            // A方血量继承
            fightParam.sideA.hpExtend = record.buildHpExtend(context.funcId, context.posNum);
            // B方血量继承
            fightParam.sideB.hpExtend = fightSideCell.hpExtend;
            context.param = fightParam;

            String instance = context.posNum == 1 ? row.instance1 : row.instance2;
            int guardLevel = context.posNum == 1 ? fightSideCell.guardLevel : fightSideCell.guardTwoLevel;

            EcResult<FightSide> fsResult = role.getGame().fight.side.createFightSideByCode(GDFight.Team.B, instance, guardLevel);
            if (!fsResult.ok()) {
                return r.fail(fsResult.message);
            }
            EcResult<PlayPO> fightResult = role.getGame().play.startByRole(role, context, fsResult.data);
            if(!fightResult.ok()){
                return r.fail(fightResult.message);
            }
            fighting = true;
            if(adheadOver(fightSideCell, fightResult.data)){
                if (this.record.stoneSkillActive) {
                    this.record.stoneSkillActive = false;
                }
                map.afterChallenge(fightSideCell.x, fightResult.data.winner == GDFight.Team.A);
            }
        }
        return r;
    }

    private void afterChallenge(FightSideCell cell, boolean win) {
        IslandChallengeResultResp resultResp = new IslandChallengeResultResp();
        resultResp.win = win;
        resultResp.over = true;
        if (win) {
            IslandGuardRow row = role.getGame().table.island.guard.get(cell.guardId);
            if (row != null) {
                role.log.setOperation(GDOperation.ISLAND_CHALLENGE_REWARD);
                // 基础奖励
                resultResp.reward = new MixRes(row.rewardStr);
                resultResp.reward.add(role, true, GDOperation.ISLAND_CHALLENGE_REWARD);
                role.log.clearOperation();
                // 记录累计奖励
                record.addReward(resultResp.reward);
                if (row.type == GDIsland.CellType.BOSS) {
                    role.getHistory().action.islandWinBossTimes();
                    role.getHistory().action.killDifficultyIslandBoss(difficulty);
                }
                role.getHistory().action.islandWin();
                role.getTrace().challengeIsland();
            }
        }
        role.sendNow(resultResp);
    }

    /** 战斗处理 */
    private GameResult<Void> challenge(FightSideCell fightSideCell, int param) {
        GameResult<Void> r = new GameResult<>();
        if(fighting){
            return r.fail("正在挑战中");
        }
        IslandGuardGroupRow row = role.getGame().table.island.guardGroup.get(fightSideCell.guardGroupId);
        if (row == null) {
            return r.fail("守卫不存在");
        }
        //放弃挑战
        if(param == -1){
            if(difficulty == GDIsland.Difficulty.NORMAL){
                return r.fail("该难度下不可放弃挑战");
            }
            return quit(fightSideCell);
        }
        FuncPosition funcPosition = role.getFight().getFuncPositions(getFightFuncId());
        if (funcPosition == null || funcPosition.positions.isEmpty()) {
            return r.fail("挑战阵容不能为空");
        }
        if (!record.checkHerosAlive(funcPosition)) {
            return r.fail("阵亡英雄不可上阵");
        }
        return beginChallenge(fightSideCell, row, null);
    }

    private GameResult<Void> quit(FightSideCell fightSideCell){
        map.afterAction(fightSideCell.x,0);
        return new GameResult<>();
    }

    private void handleGain(FightSideParam param){
        SkillAppend skillAppend =  param.skillAppend;
        // 根据增益作用范围给英雄加技能和属性
        for (Gain gain : record.getActiveGain()) {
            IslandGainRow gainRow = role.getGame().table.island.gain.get(gain.gainId);
            // 给英雄身上加技能
            FuncPosition funcPosition = role.getFight().getFuncPositions(getFightFuncId());
            for (Position position : funcPosition.positions) {
                position.grids.forEach(
                        g -> {
                            Hero hero = role.getBag().hero.getItem(g.uid);
                            if (hero == null) {
                                hero = record.searchAidHero(g.uid);
                            }
                            if (hero != null) {
                                boolean match = gainRow.match(hero.getTpl(role.toPlayBuildContext()), map.floor);
                                if (match) {
                                    if (gainRow.skillId > 0) {
                                        skillAppend.addHeroProtoSkill(hero.getTpl(role.toPlayBuildContext()).protoId, gainRow.skillId);
                                    }
                                    if (StringUtils.isNotBlank(gainRow.attrStr)) {
                                        param.attrAppend.addHeroAttr(hero.tid, new FightAttr(gainRow.attrStr));
                                        if (gain.isLevelUpGain()) {
                                            int exAttrNum = gain.getAttrNum();
                                            if (exAttrNum > 0) {
                                                param.attrAppend.addHeroAttr(hero.tid, new FightAttr(gainRow.attrStr).scale(exAttrNum));
                                            }
                                        }
                                    }
                                }
                            }
                        });
            }
        }
    }

    /** 商店 */
    private GameResult<Void> buyItem(MallCell mallCell, int index) {
        GameResult<Void> r = new GameResult<>();
        // 传-1代表放弃本次购买
        if (index == -1) {
            return r.success();
        }
        Integer itemId = mallCell.getItemsByIndex(index);
        if (itemId == null) {
            return r.fail("商品不存在");
        }
        if(mallCell.buyItems.contains(index)){
            return r.fail("商品已售罄");
        }
        IslandShopItemRow shopItemRow = role.getGame().table.island.shopItem.get(itemId);
        if (shopItemRow == null) {
            return r.fail("商品不存在");
        }
        EcResult<MixResItem> consumeRes = new MixRes(shopItemRow.consumeStr).consume(role, null);
        if (!consumeRes.ok()) {
            role.getGame().notice.tipResItem(role, consumeRes.data);
            return r.fail(consumeRes.message);
        }
        mallCell.buyItems.add(index);
        MixRes reward = new MixRes(shopItemRow.rewardStr);
        reward.add(role, true, 0);
        role.getGame().notice.dialogReward(role, reward);
        return r.success();
    }

    @Override
    public int getFuncId() {
        return GDFunc.ISLAND;
    }

    @Override
    public void onAfterOpen() {
        checkBuildMap(false);
    }

    public void noticeUpdate() {
        if(isOpen()){
            role.sendNow(new IslandDataResp(this));
        }
    }

    public EcResult<Void> gotoNextFloor(int floor){
        EcResult<Void> r = new EcResult<>();
        IslandfloorRow floorRow = map.getFloorRow();
        if (floorRow == null) {
            return r.fail("层级不存在");
        }
        if (map.notEnd()) {
            return r.fail("暂未通关");
        }
        if (!map.boxGot) {
            return r.fail("宝箱未领取");
        }
        if (!floorRow.nextFloors.contains(floor)) {
            return r.fail("层级选择错误");
        }
        EcResult<Void> ecResult = map.buildMap(floor);
        if (!ecResult.ok()) {
            return r.fail(ecResult.message);
        }
        map.noticeUpdate();
        return r;
    }

    public EcResult<Void> useStone(){
        EcResult<Void> r = new EcResult<>();
        if (record.heroStates.isEmpty()) {
            return r.fail("暂无受伤英雄");
        }
        MixRes conmuse = new MixRes();
        conmuse.addItem(new MixResItem(GDObj.Type.PROP, GDIsland.STONE_TID, 1));
        EcResult<MixResItem> consumeRes = conmuse.consume(role, null);
        if (!consumeRes.ok()) {
            role.getGame().notice.tipResItem(role, consumeRes.data);
            return r.fail(consumeRes.message);
        }
        record.useStone();
        return r.success("复活道具使用成功");
    }

    /** 查看守卫详情 */
    public EcResult<Void> watchGuardDetail(int level, int x) {
        EcResult<Void> r = new EcResult<>();
        if(difficulty == 0){
            return r.fail("请先选择难度");
        }
        return map.watchGuardDetail(level, x);
    }

    public void gmRebuildMap(){
        reset();
        noticeUpdate();
    }

    public Predicate<Hero> aliveHeroRule() {
        return record.aliveHeroRule();
    }

    public void updatePassTimes(int difficulty) {
        passTimes.put(difficulty, passTimes.getOrDefault(difficulty, 0) + 1);
    }
}
